const FLOAT_32_MAX = 3.4028234663852886e+38
const FLOAT_32_MIN = 1.1754943508222875e-38
const TWO_TO_23 = 8388608
const TWO_TO_N149 = 1.401298464324817e-45

/** 二进制写入流 */
export default class BinaryWriter {

    private static _pool: BinaryWriter[] = []
    private _bytes: number[]

    private constructor() { }

    /** 从对象池里分配一个实例,如果池里没有则新建 */
    static alloc() {
        let ins: BinaryWriter = this._pool.pop() || new BinaryWriter()
        ins._bytes = []
        return ins
    }

    /** 结束写入流并回收返回字节流 */
    end() {
        return new Uint8Array(this.recy())
    }

    /** 回收并返回bytes */
    recy() {
        let bytes = this._bytes
        this._bytes = null
        BinaryWriter._pool.push(this)
        return bytes
    }

    /** 开头插入 */
    unshift(bytes: number[]) {
        this._bytes = [...bytes, ...this._bytes]
    }

    /** 末尾连接 */
    concat(bytes: number[]) {
        this._bytes = [...this._bytes, ...bytes]
    }

    get bytes() {
        return this._bytes
    }

    get len() {
        return this._bytes.length
    }

    set byte(v: number) {
        this._bytes.push(v)
    }

    set uint8(v: number) {
        this.byte = v >>> 0 & 0xFF
    }

    set uint16(v: number) {
        this.byte = v >>> 0 & 0xFF
        this.byte = v >>> 8 & 0xFF
    }

    set uint32(v: number) {
        this.byte = v >>> 0 & 0xFF
        this.byte = v >>> 8 & 0xFF
        this.byte = v >>> 16 & 0xFF
        this.byte = v >>> 24 & 0xFF
    }

    /** 自适应长度uint32 */
    set uint(v: number) {
        while (v > 127) {
            this.byte = (v & 0x7f) | 0x80
            v >>>= 7
        }
        this.byte = v
    }

    set int8(v: number) {
        this.byte = (v >>> 0) & 0xFF
    }

    set int16(v: number) {
        this.byte = (v >>> 0) & 0xFF
        this.byte = (v >>> 8) & 0xFF
    }

    set int32(v: number) {
        this.byte = (v >>> 0) & 0xFF
        this.byte = (v >>> 8) & 0xFF
        this.byte = (v >>> 16) & 0xFF
        this.byte = (v >>> 24) & 0xFF
    }

    /** 自适应长度int32 */
    set int(v: number) {
        this.uint = (v << 1) ^ (v >> 31)
    }

    /** 32位浮点数，有精度差，要求精度数据应该使用整型乘精度再除回来 */
    set float(v: number) {
        let sign = ((v < 0) ? 1 : 0) << 31
        v = sign ? -v : v

        // 正0:负0
        if (v === 0) {
            this.int32 = (1 / v) > 0 ? 0x00000000 : 0x80000000
            return
        }

        // 非数字
        if (isNaN(v)) {
            this.int32 = 0x7FFFFFFF
            return
        }

        // 无穷数
        if (v > FLOAT_32_MAX) {
            this.int32 = (sign | (0x7F800000)) >>> 0
            return
        }

        // 非正规数
        if (v < FLOAT_32_MIN) {
            let mant = Math.round(v / TWO_TO_N149)
            this.int32 = (sign | mant) >>> 0
            return
        }

        let exp = Math.floor(Math.log(v) / Math.LN2)
        let mant = v * Math.pow(2, -exp)
        mant = Math.round(mant * TWO_TO_23) & 0x7FFFFF

        this.int32 = (sign | ((exp + 127) << 23) | mant) >>> 0
    }

    set str(v: string) {
        let bytes = []
        let c: number
        for (let i = 0, len = v.length; i < len; i++) {
            c = v.charCodeAt(i);
            if (c < 0x80) {
                bytes.push(c);
            } else if (c < 0x800) {
                bytes.push(0xC0 | (0x1F & (c >> 6)), 0x80 | (0x3F & c))
            } else if (c < 0x10000) {
                bytes.push(0xE0 | (0x0F & (c >> 12)), 0x80 | (0x3F & (c >> 6)), 0x80 | (0x3F & c))
            } else {
                bytes.push(0xF8 | (0x03 & (c >> 18)), 0x80 | (0x3F & (c >> 12)), 0x80 | (0x3F & (c >> 6)), 0x80 | (0x3F & c))
            }
        }
        this.uint = bytes.length
        this.concat(bytes)
        bytes = null
    }
}